import {useEffect, useState} from 'react';

export default class StateMapper<T> {
    private getFun: () => T;
    private cbs: Set<Function> = new Set([]);
    constructor(getFun: () => T) {
        this.getFun = getFun;
    }

    notify = () => {
        this.cbs.forEach(_ => _?.());
    };

    useMappedState = () => {
        const [_state, _setState] = useState<T>(this.getFun);
        const updateState = () => {
            _setState(this.getFun());
        };
        useEffect(() => {
            this.cbs.add(updateState);
            return () => {
                this.cbs.delete(updateState);
            };
        }, []);
        return _state;
    };
}

type UpdateFunc<T> = (prev: T) => T;

export class GlobalState<T> {
    private value: T;
    private stateMapper: StateMapper<T>;

    constructor(initValue: T) {
        this.value = initValue;
        this.stateMapper = new StateMapper(this.getValue);
    }

    public getValue = () => {
        return this.value;
    };

    public useValue = () => {
        return this.stateMapper.useMappedState();
    };

    public setValue = (value: T | UpdateFunc<T>) => {
        let newValue: T;
        if (typeof value === 'function') {
            newValue = (value as UpdateFunc<T>)(this.value);
        } else {
            newValue = value;
        }

        this.value = newValue;
        this.stateMapper.notify();
    };
}
